#pragma once

#include "comm/znx_string.h"
#include "comm/znx_queue.h"
#include "comm/znx_pool.h"
#include "comm/znx_crc.h"

#define ZNX_HTTP_ARGS_HASH_SLOT 32


typedef struct {
    uint32_t                    crc;
    u_char                      *data;
    size_t                      len;
} znx_http_arg_name_t;


typedef int(*znx_http_arg_cmp_handle)(znx_http_arg_name_t *a, znx_http_arg_name_t *b);

static inline void
znx_http_arg_name_init(znx_http_arg_name_t *name,
    u_char *data, size_t len)
{
    name->crc = znx_crc32_long_lower(data, len);
    name->data = data;
    name->len = len;
}


static inline int
znx_http_arg_cmp(znx_http_arg_name_t *a, znx_http_arg_name_t *b)
{
    if (a->crc > b->crc) {
        return 1;
    }

    if (a->crc < b->crc) {
        return -1;
    }

    if (a->len > b->len) {
        return 1;
    }

    if (a->len < b->len) {
        return -1;
    }

    return znx_strncmp(a->data, b->data, a->len);
}


typedef struct {
    znx_http_arg_name_t         name;
    znx_str_t                   val;
    znx_queue_t                 queue;
    znx_queue_t                 queue_in_hash;

    /* if value not empty, the has_delimiter flag will be ignore. */
    unsigned                    has_delimiter:1;
} znx_http_arg_t;


typedef struct {
    znx_queue_t                 head;
    znx_queue_t                 hash_lists[ZNX_HTTP_ARGS_HASH_SLOT];
    znx_queue_t                 free_head;
    znx_http_arg_cmp_handle     cmp_handle;
} znx_http_args_t;


static inline void
znx_http_args_init(znx_http_args_t *args)
{
    znx_queue_init(&args->head);
    for (int i = 0; i < ZNX_HTTP_ARGS_HASH_SLOT; i++) {
        znx_queue_init(&args->hash_lists[i]);
    }
    znx_queue_init(&args->free_head);
    args->cmp_handle = znx_http_arg_cmp;
}


// add the arg to the tail of the queue.
void znx_http_args_add(znx_http_args_t *args, znx_http_arg_t *arg);


// use the arg to cover all same args.
// if no arg with the same name exits, will add the arg to the tailf of the queue.
void znx_http_args_set(znx_http_args_t *args, znx_http_arg_t *arg);


// get the i-th arg with the same name.
// to get the first arg, just pass in 0.
znx_http_arg_t *znx_http_args_get(znx_http_args_t *args,
    znx_http_arg_name_t *name, size_t index);


// delete the first matching header.
void znx_http_args_del_once(znx_http_args_t *args,
    znx_http_arg_name_t *name);


// delete all matching args.
void znx_http_args_del(znx_http_args_t *args,
    znx_http_arg_name_t *name);


znx_http_arg_t *
znx_http_args_get_free_arg(znx_http_args_t *args);


// parser args str to http_args
// supports string parsing in the following format.
// 1. ""            ==> {}
// 2. "a"           ==> {"a": ""}
// 3. "a="          ==> {"a": ""}
// 4. "=1"          ==> {"": "1"}
// 5. "a=&b=1"      ==> {"a": "", "b": "1"}
// 6. "a&b=1"       ==> {"a": "", "b": "1"}
// 7. "=1&b=1"      ==> {"": "1", "b": "1"}
// 8. "&b=1"        ==> {"": "",  "b": "1"}
// 9. "&&b=1"       ==> {"": "",  "": "",  "b": "1"}
void znx_http_args_parse(znx_pool_t *pool, znx_http_args_t *args, znx_str_t *args_str);


// real data space is apploed from the pool.
void znx_http_args_to_string(znx_pool_t *pool, znx_http_args_t *args, znx_str_t *dst_str);
